package com.gitee.zycra.future.job.service;

import com.gitee.zycra.future.job.common.bo.JobTriggerBO;
import com.gitee.zycra.future.job.common.container.WebSocketSessionContainer;
import com.gitee.zycra.future.job.common.dto.JobTriggerDTO;
import com.gitee.zycra.future.job.common.dto.NodeStatusDTO;
import com.gitee.zycra.future.job.common.enums.JobCycleEnum;
import com.gitee.zycra.future.job.common.enums.ScheduleRatioEnum;
import com.gitee.zycra.future.job.common.model.JobNode;
import com.gitee.zycra.future.job.common.model.JobRunning;
import com.gitee.zycra.future.job.common.model.JobSchedule;
import com.gitee.zycra.future.job.common.util.DateUtil;
import com.gitee.zycra.future.job.common.util.JSONUtil;
import com.gitee.zycra.future.job.common.vo.JobNodeVO;
import com.gitee.zycra.future.job.manager.JobDependenceManager;
import com.gitee.zycra.future.job.manager.JobNodeManager;
import com.gitee.zycra.future.job.manager.JobRunningManager;
import com.gitee.zycra.future.job.manager.JobScheduleLogManager;
import com.gitee.zycra.future.job.manager.JobScheduleManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zycra
 */
@Slf4j
@Service
public class JobNodeService {

    @Autowired
    private JobNodeManager jobNodeManager;

    @Autowired
    private JobRunningManager jobRunningManager;

    @Autowired
    private JobScheduleManager jobScheduleManager;

    @Autowired
    private JobDependenceManager jobDependenceManager;

    @Autowired
    private JobScheduleLogManager jobScheduleLogManager;

    @Autowired
    private WebSocketSessionContainer webSocketSessionContainer;

    /**
     * 同步节点状态
     *
     * @param nodeStatusDTO   节点状态对象
     * @param applicationName 应用名称
     * @param nodeIp          节点IP地址
     */
    public void syncNodeStatus(NodeStatusDTO nodeStatusDTO, String applicationName, String nodeIp) {
        JobNode newNode = new JobNode()
                .setNodeIp(nodeIp)
                .setApplicationName(applicationName)
                .setAlive(true)
                .setCpuUsedRatio(nodeStatusDTO.getCpuUsedRatio())
                .setMemoryUsedRatio(nodeStatusDTO.getMemoryUsedRatio())
                .setLastHeartBeatsTime(DateUtil.getNowLocalDateTime())
                .setRunningCount(nodeStatusDTO.getRunningList().size());
        List<JobNode> jobNodeList = jobNodeManager.selectByApplicationAndIp(applicationName, nodeIp);
        if (CollectionUtils.isEmpty(jobNodeList)) {
            jobNodeManager.insertJobNode(newNode);
            return;
        }
        newNode.setId(jobNodeList.get(0).getId());
        jobNodeManager.updateJobNode(newNode);
    }

    /**
     * 通知节点运行
     */
    public void notifyNode() {
        List<JobTriggerBO> triggerList = jobRunningManager.selectJobForTrigger();
        for (JobTriggerBO jobTriggerBO : triggerList) {
            // 依赖任务执行情况检查
            boolean enableExecute = true;
            List<Long> parentIdList = jobDependenceManager.selectParentIdByChildScheduleId(jobTriggerBO.getJobScheduleId());
            if (!CollectionUtils.isEmpty(parentIdList)) {
                Integer jobCycle = jobScheduleManager.selectJobCycleByJobScheduleId(jobTriggerBO.getJobScheduleId());
                LocalDateTime startTime = jobTriggerBO.getScheduleTime().minusMinutes(JobCycleEnum.getByCode(jobCycle).getMinutes());
                List<Long> successList = jobScheduleLogManager.selectSuccessByIdList(parentIdList, startTime);
                parentIdList.removeAll(successList);
                if (!CollectionUtils.isEmpty(parentIdList)) {
                    jobScheduleManager.updateJobSchedule(new JobSchedule()
                            .setId(jobTriggerBO.getJobScheduleId())
                            .setLastScheduleTime(jobTriggerBO.getScheduleTime()));
                    jobRunningManager.updateJobRunning(new JobRunning().setId(jobTriggerBO.getRunningId())
                            .setScheduleRatio(ScheduleRatioEnum.PARENT_NOT_SUCCESS.getCode())
                            .setCompleteTime(DateUtil.getNowLocalDateTime()));
                    enableExecute = false;
                }
            }

            WebSocketSession session = webSocketSessionContainer.getSession(jobTriggerBO.getApplicationName(), jobTriggerBO.getNodeIp());
            if (!enableExecute || session == null) {
                continue;
            }
            JobTriggerDTO jobTriggerDTO = JobTriggerBO.convertToJobTriggerDTO(jobTriggerBO);
            log.info("调度通知开始, jobTriggerDTO={}", jobTriggerDTO);
            try {
                session.sendMessage(new TextMessage(JSONUtil.toJSONString(jobTriggerDTO)));
                jobScheduleManager.updateJobSchedule(new JobSchedule()
                        .setId(jobTriggerBO.getJobScheduleId())
                        .setLastScheduleTime(jobTriggerBO.getScheduleTime()));
                jobRunningManager.updateJobRunning(new JobRunning().setId(jobTriggerBO.getRunningId())
                        .setScheduleRatio(ScheduleRatioEnum.TRIGGER_SUCCESS.getCode()));
            } catch (Exception e) {
                log.error("调度异常, ", e);
                jobRunningManager.updateJobRunning(new JobRunning().setId(jobTriggerBO.getRunningId())
                        .setScheduleRatio(ScheduleRatioEnum.TRIGGER_FAILED.getCode())
                        .setCompleteTime(DateUtil.getNowLocalDateTime()));
            }
        }
    }

    /**
     * 同步节点状态
     */
    public void syncNodeRunningStatus() {
        jobNodeManager.checkAliveAndUpdate();
    }

    /**
     * 查询节点信息
     *
     * @param applicationName 应用名称
     * @param nodeIp          节点IP
     * @return 查询结果
     */
    public List<JobNodeVO> selectByApplicationAndIp(String applicationName, String nodeIp) {
        List<JobNode> jobNodeList = jobNodeManager.selectByApplicationAndIp(applicationName, nodeIp);
        List<JobNodeVO> result = new ArrayList<>();
        for (JobNode jobNode : jobNodeList) {
            result.add(JobNodeVO.convertToJobNodeVO(jobNode));
        }
        return result;
    }
}
